package com.ruoyi.web.controller.app.user;


import cn.dev33.satoken.annotation.SaIgnore;
import cn.dev33.satoken.secure.BCrypt;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateUtil;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.ruoyi.app.domain.bo.AppLevelBo;
import com.ruoyi.app.domain.bo.AppPackageOrderBo;
import com.ruoyi.app.domain.bo.AppSalesLevelBo;
import com.ruoyi.app.domain.bo.AppUserBo;
import com.ruoyi.app.domain.vo.*;
import com.ruoyi.app.service.*;
import com.ruoyi.common.annotation.RateLimiter;
import com.ruoyi.common.annotation.RepeatSubmit;
import com.ruoyi.common.constant.CacheConstants;
import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.constant.TaskConstants;
import com.ruoyi.common.constant.UserConstants;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.PageQuery;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.common.core.domain.model.LoginUser;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.common.enums.LimitType;
import com.ruoyi.common.enums.LoginType;
import com.ruoyi.common.exception.user.CaptchaException;
import com.ruoyi.common.exception.user.CaptchaExpireException;
import com.ruoyi.common.helper.LoginHelper;
import com.ruoyi.common.utils.InfoUtils;
import com.ruoyi.common.utils.ServletUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.VerifyCodeUtils;
import com.ruoyi.common.utils.redis.RedisUtils;
import com.ruoyi.common.utils.spring.SpringUtils;
import com.ruoyi.im.core.TxTemplate;
import com.ruoyi.sms.config.properties.SmsProperties;
import com.ruoyi.system.service.AppLoginService;
import com.ruoyi.system.service.ISysConfigService;
import com.ruoyi.web.filter.SensitiveFilter;
import com.ruoyi.web.service.CaptchaService;
import io.netty.util.Timeout;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.client.RestTemplate;

import javax.servlet.http.HttpServletRequest;
import java.math.BigDecimal;
import java.time.Duration;
import java.util.*;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

/**
 * 用户信息控制器
 *
 * @author heike
 */
@Slf4j
@RequiredArgsConstructor
@RestController
@RequestMapping("/app/user")
public class AppUserController extends BaseController {

    @Autowired
    private RestTemplate restTemplate;

    @Autowired
    private TxTemplate txTemplate;

    private final SmsProperties smsProperties;
    private final IAppTaskService appTaskService;
    private final IAppUserService appUserService;
    private final AppLoginService appLoginService;
    private final IAppLevelService appLevelService;
    private final IAppAssetService appAssetService;
    private final ISysConfigService sysConfigService;
    private final IAppFriendService iAppFriendService;
    private final IAppSalesLevelService appSalesLevelService;
    private final IAppPackageOrderService appPackageOrderService;



    /**
     * 筛选用户根据手机号或者id
     *
     * @return 用户信息
     */
    @GetMapping("/selUserByPhoneAndId")
    public R<?> selUserByPhoneAndId(@RequestParam(name = "keyWord", required = false) String keyWord) {
        List<AppUserVo> userVo = appUserService.selUserByPhoneAndId(keyWord);
        return R.ok(userVo);
    }


    /**
     * 获取当前登录用户信息
     *
     * @return 用户信息
     */
    @GetMapping()
    public R<Object> userInfo(@RequestParam(name = "id", required = false) Long id) {
        id = id == null ? getUserId() : id;
        UserVo userVo = appUserService.setUserInfo(id, getUserId());
        if (userVo.getSearchType()==0){
            userVo.setSearchId(0);
            userVo.setSearchPhone(0);
        }
        if (userVo.getSearchType()==1){
            userVo.setSearchId(0);
            userVo.setSearchPhone(1);
        }
        if (userVo.getSearchType()==2){
            userVo.setSearchId(1);
            userVo.setSearchPhone(0);
        }
        if (userVo.getSearchType()==3){
            userVo.setSearchId(1);
            userVo.setSearchPhone(1);
        }
        return R.ok(userVo);
    }

    /**
     * 获取当前登录用户信息
     *
     * @return 用户信息
     */
    @GetMapping("chatNameInfo")
    public R<Object> chatNameInfo(@RequestParam(name = "chatName") String chatName) {
        Long id = userId(chatName);
        UserVo userVo = appUserService.setUserInfo(id, getUserId());
        return R.ok(userVo);
    }

    /**
     * 获取腾讯用户名获取基本信息
     *
     * @return 用户信息
     */
    @GetMapping("chatInfo")
    public R<Object> userInfo(@RequestParam(name = "chatName") String chatName) {
        Long aLong = userId(chatName);
        UserVo userVo = appUserService.queryByIdUserVo(aLong);
        userVo.setIsRelation(iAppFriendService.userRelation(getUserId(), aLong));
        return R.ok(userVo);
    }


    /**
     * 获取用户的邀请码和邀请用户列表
     *
     * @return
     */
    @RepeatSubmit(interval = 2000)
    @GetMapping("inviteCode")
    public R<Object> inviteCode() {
        Long id = getUserId();
        String key = CacheConstants.INVITE_CODE_KEY + id;
//        if (RedisUtils.hasKey(key)){
//            HashMap<String, Object> cacheMap = (HashMap<String, Object>) RedisUtils.getCacheObject(key);
//            System.out.println("从缓存获取：" + cacheMap);
//            return R.ok(cacheMap);
//        }
        HashMap<String, Object> stringObjectHashMap = appUserService.inviteCode(id);
        //RedisUtils.setCacheObject(key, stringObjectHashMap, Duration.ofMinutes(10));
        return R.ok(stringObjectHashMap);
    }





    /**
     * 获取用户的邀请二维码
     *
     * @return
     */
    @RepeatSubmit(interval = 2000)
    @GetMapping("inviteCodeImage")
    public R<Object> inviteCodeImage() {
        Long id = getUserId();
        HashMap<String, Object> stringObjectHashMap = appUserService.inviteCodeImage(id);
        return R.ok(stringObjectHashMap);
    }


    /**
     * 获取用户的邀请列表和间接邀请列表
     *
     * @return
     */
    @RepeatSubmit(interval = 2000)

    @GetMapping("/inviteUserPage")
    public TableDataInfo<AppUserVo> inviteUserPage(String realStatus, String type, PageQuery pageQuery) {
        List<AppLevelVo> appLevelRateVos = appLevelService.queryList(new AppLevelBo());
        List<AppSalesLevelVo> salesLevelVos = appSalesLevelService.queryList(new AppSalesLevelBo());
        Map<Long, String> level = appLevelRateVos.stream().collect(Collectors.toMap(AppLevelVo::getId, AppLevelVo::getName));
        Map<Long, AppSalesLevelVo> saLesLevel = salesLevelVos.stream().collect(Collectors.toMap(AppSalesLevelVo::getId, i -> i));
        Long id = getUserId();
        AppUserBo appUserBo = new AppUserBo();
        appUserBo.setParentId(id);
        appUserBo.setRealStatus(realStatus);
        TableDataInfo<AppUserVo> users = appUserService.queryPageList(appUserBo, pageQuery);
        users.getData().forEach(e -> {
            e.setPassword(null);
            e.setUniqueCode(null);
            List<AppUserVo> allUserVos = appUserService.allList(e.getId());
            AppPackageOrderBo packageOrderBo = new AppPackageOrderBo();
            packageOrderBo.setUserId(e.getId());
            List<AppPackageOrderVo> appPackageOrderVos = appPackageOrderService.queryList(packageOrderBo);
            e.setProgress(allUserVos.size() + "/" + appPackageOrderVos.size());
            e.setLevelName(level.get(e.getLevel()));
            e.setSalesLevelVo(saLesLevel.get(e.getSalesLevel()));
            AppAssetVo appAssetVo = appAssetService.queryByUid(e.getId());
            if (Objects.nonNull(appAssetVo)) {
                e.setLiveness(appAssetVo.getLiveness());
            } else {
                e.setLiveness(BigDecimal.ZERO);
            }
            e.setCreateTimeFormat(DateUtil.format(e.getCreateTime(), "yyyy-MM-dd HH:mm:ss"));
        });
        if (type.equals(Constants.SUCCESS)) {
            return users;
        }
        List<AppUserVo> appUserVos = appUserService.selectList(appUserBo);
        List<Long> collect = appUserVos.stream().map(AppUserVo::getId).collect(Collectors.toList());
        if (!collect.isEmpty()) {
            AppUserBo userBo = new AppUserBo();
            userBo.setUserIds(collect);
            userBo.setRealStatus(realStatus);
            TableDataInfo<AppUserVo> indirectUsers = appUserService.queryPageList(userBo, pageQuery);
            indirectUsers.getData().forEach(e -> {
                e.setPassword(null);
                e.setUniqueCode(null);
                List<AppUserVo> allUserVos = appUserService.allList(e.getId());
                AppPackageOrderBo packageOrderBo = new AppPackageOrderBo();
                packageOrderBo.setUserId(e.getId());
                List<AppPackageOrderVo> appPackageOrderVos = appPackageOrderService.queryList(packageOrderBo);
                e.setProgress(allUserVos.size() + "/" + appPackageOrderVos.size());
                e.setLevelName(level.get(e.getLevel()));
                e.setSalesLevelVo(saLesLevel.get(e.getSalesLevel()));
                AppAssetVo appAssetVo = appAssetService.queryByUid(e.getId());
                if (Objects.nonNull(appAssetVo)) {
                    e.setLiveness(appAssetVo.getLiveness());
                } else {
                    e.setLiveness(BigDecimal.ZERO);
                }
                e.setCreateTimeFormat(DateUtil.format(e.getCreateTime(), "yyyy-MM-dd HH:mm:ss"));
            });
            return indirectUsers;
        }
        return TableDataInfo.build(new ArrayList<>());
    }

    /**
     * 校验验证码
     *
     * @param phonenumber 手机号
     * @param code        手机验证码
     * @return
     */
    @GetMapping("checkMsg")
    public R<Object> checkMsg(
        @RequestParam(name = "code") String code,
        @RequestParam(name = "phonenumber") String phonenumber
    ) {
        String key = CacheConstants.CAPTCHA_CODE_KEY + phonenumber;
        String msCode = RedisUtils.getCacheObject(key);
        if (smsProperties.getEnabled() && StringUtils.isBlank(msCode)) {
            throw new CaptchaExpireException();
        }
        if (smsProperties.getEnabled() && !msCode.equals(code)) {
            throw new CaptchaException();
        }
        return R.ok();
    }

    /**
     * 修改绑定手机
     *
     * @param phonenumber 手机号
     * @return
     */
    @GetMapping("updateMobile")
    public R<Object> updateMobile(
        @RequestParam(name = "phonenumber") String phonenumber) {
        String regex = "^1\\d{10}$";
        if (!Pattern.matches(regex, phonenumber)) {
            return R.fail("手机号码格式错误!");
        }
        AppUserVo appUserVo = appUserService.queryByPhone(phonenumber);
        if (Objects.nonNull(appUserVo)) {
            return R.fail("手机号已经被其他账号使用，请直接登录！");
        }
        AppUserVo appUserVo1 = appUserService.queryById(getUserId());
        appUserVo1.setPhone(phonenumber);
        appUserVo1.setUserName(phonenumber);
        return R.ok(appUserService.updateByVo(appUserVo1));
    }

    /**
     * 绑定邀请用户邀请码
     *
     * @param inviteCode 邀请码
     * @return
     */
    @PostMapping("bindInviteCode")
    public R<String> bindInviteCode(String inviteCode) {
        AppUserVo appUserVo = appUserService.queryByInviteCode(inviteCode.toUpperCase());
        if (Objects.isNull(appUserVo)) {
            return R.fail("邀请码不存在!");
        }
        if (appUserVo.getId().equals(LoginHelper.getUserId())) {
            return R.fail("不能绑定自己的邀请码!");
        }
        AppUserVo user = appUserService.queryById(getUserId());
        if (user.getParentId() != null) {
            return R.fail("账号已经绑定邀请码!");
        }
        appTaskService.downTask(null, TaskConstants.invite, getUserId());
        return R.result(appUserService.updateParentAndAward(user.getId(), appUserVo.getId()));
    }

    /**
     * 修改用户信息
     *
     * @param bo 用户信息对象
     * @return
     */
    @PostMapping("updateInfo")
    public R<Object> updateInfo(AppUserBo bo) {
        bo.setId(getUserId());
        SensitiveFilter sensitiveFilter = SensitiveFilter.DEFAULT;
        if (StringUtils.isNotBlank(bo.getNickName())) {
            bo.setNickName(sensitiveFilter.filter(bo.getNickName(), '*'));
        }
        if (StringUtils.isNotBlank(bo.getSignature())) {
            bo.setSignature(sensitiveFilter.filter(bo.getSignature(), '*'));
        }
        if (bo.getBirthday() != null) {
            int age = InfoUtils.getAge(bo.getBirthday());
            if (age < UserConstants.AGE) {
                return R.fail("年龄不能低于18岁！");
            }
        }

        //更改搜索类型
        if (bo.getSearchId() != null && bo.getSearchPhone()!=null){
            if (bo.getSearchId() == 1 && bo.getSearchPhone() == 1){
                bo.setSearchType(3);
            }
            if (bo.getSearchId() == 1 && bo.getSearchPhone() == 0){
                bo.setSearchType(2);
            }
            if (bo.getSearchId() == 0 && bo.getSearchPhone() == 1){
                bo.setSearchType(1);
            }
            if (bo.getSearchId() == 0 && bo.getSearchPhone() == 0){
                bo.setSearchType(0);
            }
        }


        if (appUserService.updateInfo(bo)) {
            if (StringUtils.isNotBlank(bo.getNickName()) || StringUtils.isNotBlank(bo.getAvatar())) {
                TxTemplate imTemplate = SpringUtils.getBean(TxTemplate.class);
                imTemplate.updateUserInfo(bo.getId().toString(), bo.getNickName(), bo.getAvatar());
            }
            return R.ok();
        }
        return R.fail();
    }

    /**
     * 修改密码
     *
     * @param password 修改的新密码
     * @param code     验证码
     * @return
     */
    @PostMapping("updatePassword")
    public R<String> updatePassword(String password,String surePassword) {
        LoginUser user = LoginHelper.getLoginUser();
        AppUserVo appUserVo = appUserService.queryById(user.getUserId());
        String password1 = appUserVo.getPassword();
        if (password1!=null){
            if (!BCrypt.checkpw(password1, password)) {
                return R.fail("修改密码失败，旧密码错误");
            }
            if (BCrypt.checkpw(surePassword, password)) {
                return R.fail("新密码不能与旧密码相同");
            }
        }
        return R.result(appUserService.updatePassword(user.getUserId(), BCrypt.hashpw(password)));
    }


    /**
     * 发送短信验证码
     *
     * @param phonenumber 手机号码
     */
    @SaIgnore

    @PostMapping("sendMessage")
    public R<Object> sendMessage(
        @RequestParam(name = "phonenumber") String phonenumber
    ) {
        String regex = "^1\\d{10}$";
        if (!Pattern.matches(regex, phonenumber)) {
            return R.fail("手机号码格式错误!");
        }
        String key = CacheConstants.CAPTCHA_CODE_KEY + phonenumber;
        String sendCode = RedisUtils.getCacheObject(key);
        if (StringUtils.isNotBlank(sendCode)) {
            return R.fail("验证码已发送，请稍后在再试");
        }
        String code = VerifyCodeUtils.generateVerifyNumberCode(4);
        HashMap<String, Object> data = new HashMap<>();
        data.put("appid", smsProperties.getAccessKeyId());
        data.put("key", smsProperties.getAccessKeySecret());
        data.put("code", code);
        data.put("mobile", phonenumber);
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_JSON);
        HttpEntity<HashMap<String, Object>> formEntity = new HttpEntity<>(data, headers);
        String body = restTemplate.postForEntity(smsProperties.getEndpoint(), formEntity, String.class).getBody();
        System.out.println("发送短信返回信息:" + body);
        JSONObject jsonObject = JSONObject.parseObject(body);
        boolean result = jsonObject.get("code").toString().equals(Constants.FAIL);
        if (result) {
            //缓存短信验证码
            RedisUtils.setCacheObject(key, code, Duration.ofMinutes(Constants.APP_CAPTCHA_EXPIRATION));
            log.info("手机号码[{}]的验证码是：{}", phonenumber, code);
            return R.ok("发送短信成功");
        } else {
            return R.fail("发送短信失败!");
        }
    }


    /**
     * 注销账号
     *
     * @return
     */
    @PostMapping("logOff")
    public R<Void> logOff(@RequestParam String code, @RequestParam String phone, HttpServletRequest request) {
        if (!"905029".equals(code)){
            appLoginService.checkLogin(LoginType.SMS, phone, () -> !appLoginService.validateSmsCode(phone, code, request));
        }
        AppUserVo userVo = appUserService.queryById(getUserId());
        if (userVo.getStatus().equals(Constants.TWO)) {
            return R.fail("账号已注销！");
        }
        String key = CacheConstants.LOGIN_OUT + userVo.getPhone();
        long time = CacheConstants.LOGIN_OUT_DAY * 86400L;
        if (appUserService.updateStatus(userVo.getId(), Constants.TWO)) {
            RedisUtils.setCacheObject(key, time, Duration.ofSeconds(time));
        }
        appLoginService.logout();
        return R.ok("注销成功");
    }


    /**
     * 注销账号测试
     *
     * @return
     */
    @GetMapping("/checkLogOff")
    public R<?> checkLogOff() {
        Long id = getUserId();
        HashMap<String, Object> map = new HashMap<>();
        map.put("secure", "1");
        AppAssetVo assetVo = appAssetService.queryByUid(id);
        if (assetVo.getBalance().compareTo(BigDecimal.ZERO) <= 0 &&
            assetVo.getIntegral().compareTo(BigDecimal.ZERO) <= 0
        ) {
            map.put("asset", "1");
        } else {
            map.put("asset", "0");
        }
        return R.ok(map);
    }


    /**
     * 获取用户的IM签名
     *
     * @return
     */
    @GetMapping("/sign")
    public R<?> getUserSign() {
        String userId = LoginHelper.getUserId().toString();
        HashMap<String, Object> map = new HashMap<>();
        map.put("userSign", txTemplate.getUserSign(userId));
        return R.ok(map);
    }

    /**
     * 显示隐藏阅后即焚
     *
     * @return
     */
    @SaIgnore
    @GetMapping("showAndHidden")
    public R<HashMap<String, Object>> showAndHidden() {
        HashMap<String, Object> map = new HashMap<>(2);
        map.put("status", sysConfigService.selectConfigByKey("app.chat.show"));
        return R.ok(map);
    }
}
